package thinker.miracle.datasource;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author bo.chen
 *
 */
public class DataSourceAspect {

	/** 
     * 缓存 已经选择过的数据源配置
     */  
    private static ConcurrentHashMap<String, Boolean> methodIsReadCache = new ConcurrentHashMap<>();  

    /**
     * 选择数据源
     * 
     * @param pjp	poi对象，（spring切面）
     * @return
     * @throws Throwable
     * 
     * 1.从缓存中获取数据源配置，如果没有根据拦截的方法是否有事物，有事物，则配置主库数据源，没有事物，则配置从库数据
     */
    public Object dateSourcePointcut(ProceedingJoinPoint pjp) throws Throwable {  
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();  
        Object target = pjp.getTarget();  
        String cacheKey = target.getClass().getName() + "." + method.getName();
        Boolean isReadCacheValue = methodIsReadCache.get(cacheKey);  
        if (isReadCacheValue == null) {  
            // 重新获取方法，否则传递的是接口的方法信息  
            Method realMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());  
            isReadCacheValue = isChoiceSlave(realMethod);  
            methodIsReadCache.put(cacheKey, isReadCacheValue);  
        }
        if (isReadCacheValue) {  
            DynamicDataSourceHolder.markSlave();  
        } else {  
            DynamicDataSourceHolder.markMaster();  
        }  
        try {  
            return pjp.proceed();  
        } finally {  
            DynamicDataSourceHolder.reset();
        }  
    }  
  
    /**
     * 判断当前拦截的方法是否是从库数据源
     * 
     * @param method	执行的方法名称
     * @return
     * 	true : 从库数据源
     * 	false: 主库数据源
     */
    private boolean isChoiceSlave(Method method) {  
        Transactional transactionalAnno = AnnotationUtils.findAnnotation(method, Transactional.class);  
        if (transactionalAnno == null) {  
            return true;  
        }  
        //如果之前选择了主库，则现在还选择主库  
        if (DynamicDataSourceHolder.isChoiceSlave()) {  
            return false;  
        }  
        if (transactionalAnno.readOnly()) {  
            return true;  
        }  
        return false;  
    }  
}
